Skip to content

Owners manage their caches: services/caches/create|update (OCDE), archive|disable|enable - #657

Draft
hxdimpf wants to merge 15 commits into
opencaching:masterfrom
OpencachingDeutschland:up/caches-crud
Draft

hxdimpf wants to merge 15 commits into
opencaching:masterfrom
OpencachingDeutschland:up/caches-crud

Conversation

@hxdimpf

@hxdimpf hxdimpf commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

The sixth and last of the changes from OpencachingDeutschland/okapi, cut into separate PRs (see #632).

Draft, stacked on #654, #655 and #656. Those three PRs are also in this branch, because all of them add to OkapiServiceRunner and the tests. Once they are merged, I'll take this out of draft and rebase it, so that only its own commits remain.

What it is for

The main use case is a smartphone app. A player should be able to play the whole game from the phone, and hiding a cache is part of the game. Out in the field, with the container just placed, the owner needs to:

  • create a simple listing;
  • correct a detail later;
  • take the cache out of play for a while, put it back, or archive it.

These methods cover exactly that.

They will never match the website, and they are not meant to. The website stays the place for everything beyond a simple listing. The official Geocaching app makes a similar cut: it takes descriptions in plain text only ("the app does not support rich text formatting") and sends everything else to the website. These methods follow that model:

Through OKAPI (these methods) Only on the website
A new listing of any type: name, coordinates, difficulty, terrain, size, short description, plain-text description, hint, log password, hidden date, additional waypoints Formatted descriptions (HTML, the editor)
Changing those fields of an own cache Descriptions in further languages
Disable, enable, archive (status logs) Listing images, attributes, cache lists
Changing a description with formatting beyond paragraphs and line breaks (it is left alone, never overwritten)
Everything for OC team members: locking, adoption, reviews

The methods

  • services/caches/create (level 3, OCDE only) creates a cache owned by the user, with the rules of the website's "hide a cache" form:
    • any cache type the installation supports;
    • name, type, coordinates, difficulty, terrain and size are required; short description, plain-text description, hint, language, country, hidden date, log password and additional waypoints of every type are optional;
    • publish: now (the default), later (at a given day and full hour), or notnow, as on the website, for every cache type. With notnow the owner finishes the listing on the website and publishes it there. An unpublished cache isn't visible through OKAPI, not even to its owner, but update works on it;
    • the website's date rules (a cache must be hidden by the time it is published, an event must take place after it), and its refusal of a second active cache at the same coordinates;
    • it returns the new cache_code and the URL of the cache page.
  • services/caches/update (level 3, OCDE only, owner only) changes those fields of an own cache, and the log password.
    • Waypoints can be replaced all at once, added, or removed by the names services/caches/geocache returns.
    • Only values that differ from the stored ones are written. The response lists the changed parameters, so a client may send back everything it has read.
    • A description is replaced only if it has no formatting: plain, as OKAPI writes it, or only paragraphs and line breaks (<p>, <br>) as the website's editor writes a few lines of text. One with more formatting (links, images, bold, lists) is refused and stays as it is.
    • It changes no status; services/caches/edit stays exactly as it is.
  • services/caches/archive, services/caches/disable, services/caches/enable (level 3, owner only, every installation) write the status log entry of services/logs/submit ("Archived", "Temporarily unavailable", "Ready to search"), with its rules. In addition:
    • A change to the status the cache already has is refused, so no log is written for nothing.
    • The comment is the owner's own text, plain text only; OKAPI adds none.
    • Unarchiving follows each site: OCDE allows it, OCPL doesn't.
  • Additional waypoints are public. On OCDE everyone sees every additional waypoint with its coordinates, as on the website, which has no way to hide one. So a final waypoint gives the final away. The methods can do it; the docs of create and update warn against it. (OCPL can hide waypoints; OCDE can't yet.)
  • Texts: descriptions and hints are plain text, escaped the way the website escapes them, and stored like the website's own descriptions.
  • New flag has_cache_editing in apisrv/installation, like has_draft_logs and has_lists. It covers create and update. Older installations don't return it; treat that as false.
  • On OCPL: has_cache_editing is false, and create and update return HTTP 400 pointing to the flag. archive, disable and enable work there as OCPL's status logs do. Nothing else changes for OCPL.

Same rules in two places (@kojoty's concern in #570): for now, the website keeps its own code and full control, and these methods follow its rules (option b in the discussion below). The longer-term aim is one place that holds the rules for everyone (option c).

Review history: andrixnet's reviews on 2026-10-19 and 2026-10-20 (see the comments below).

Tested with the suite from #653 (this branch, including #654 to #656), on an OCDE installation and two OCPL installations (the official 2018 devel dump with all updates, on MariaDB 10.11 and on MariaDB 10.0), PHP 8.2-FPM behind Apache:

read-only --write
OCDE 13 passed, 0 failed 23 passed, 0 failed, 4 skipped
OCPL, MariaDB 10.11 12 passed, 0 failed 16 passed, 0 failed
OCPL, MariaDB 10.0 12 passed, 0 failed 16 passed, 0 failed

On OCDE the write run does the following with a test cache:

  • creates it with parking, point-of-interest, stage and final waypoints, and checks that seven rules refuse invalid input (the website's, and unknown cache and waypoint types);
  • sends a Traditional, a Multi and a Mystery unpublished, checks that OKAPI doesn't show them, and updates them;
  • updates it, including the description and the log password, and removes a waypoint;
  • checks that unchanged values change nothing;
  • checks that a status change to the current status is refused.

My OCDE setup runs OKAPI without the website, so it can't write log entries; the status logs were tested on OCPL, with the test user as the temporary owner of a test cache:

  • disable, enable and archive wrote log types 11, 10 and 9;
  • the current status and unarchiving were refused;
  • HTML in the comment was stored escaped.

A formatted description was refused, and the other fields could still be changed. On every installation, a status change on someone else's cache is refused. All changed PHP files pass php -l on PHP 7.4. No database changes.

@kojoty, @jrmajor, @andrixnet, @stefopl: reviews are welcome. It changes OKAPI itself, so it stays open for review and gives OCPL time to look before anything is merged.

@hxdimpf

hxdimpf commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Unlike #654 to #656, this one was never reviewed before, and it asks two open questions (above). I'll merge it only once those two questions are settled; if they are by Monday, 2 November 2026, it goes in that day, right after #654 to #656, rebased onto them. Until then, reviews and answers to the questions are very welcome.

@jrmajor

jrmajor commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Please create stacked PRs in GitHub (https://docs.github.com/en/pull-requests/how-tos/create-pull-requests/creating-stacked-pull-requests), target master directly or wait for the previous changes to be merged. It's hard to review when the diff is thousands of lines long, and most changes are from other PRs.

@hxdimpf
hxdimpf marked this pull request as draft October 19, 2026 14:37
@hxdimpf

hxdimpf commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

You're right, @jrmajor, sorry for the huge diffs. Pointing to the last commit in the description wasn't enough.

I've turned #655, #656 and this PR into drafts. They'll come up one at a time: when the PR before is merged, the next one is taken out of draft, rebased if needed, and then shows only its own changes. So for now only #654 is up for review.

@andrixnet

andrixnet commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

@kojoty, @jrmajor, @andrixnet, @stefopl: could you review this, and say what you think of the three questions?

What is the third question?

if ($latitude < -90 || $latitude > 90)
throw new InvalidParam('latitude', 'latitude must be between -90 and 90.');
}
if ($longitude !== null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not checking !== '' also ?


$size_int = null;
if ($size2 !== null && $size2 !== '') {
$size_map = ['none' => 7, 'nano' => 8, 'micro' => 2, 'small' => 3,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to refactor these too sometime :-)

@andrixnet

Copy link
Copy Markdown
Contributor

I think once implemented, making it work also for OCPL in the future should be a much easier task.

@andrixnet

Copy link
Copy Markdown
Contributor

Some concerns from @kojoty here #570 (comment) seem to be well addressed in caches/update method, as the method changes only explicitely given parameters.

An update method that required all cache parameters to be present would have posed greater problems.

There is another catch: third party apps may implement the functionality in two ways:

  • retrieve all cache data; user makes changes; submit only changed fields
  • retrieve all cache data; user makes changes; submit everything, including apparently unchanged fields.

I say "apparently" because there is no guarantee that the app won't do some internal processing / conversion on at least some of the fields, though, "apparently" no modification is visible.
This one perhaps would most the text fields, like description ... ?

try {
# Update caches table
if (!empty($update_parts)) {
Db::query("UPDATE caches SET "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume the "last modified" information is handled by a trigger?

if ($hint2 !== null && $hint2 !== '')
$desc_parts[] = "hint = '".Db::escape_string($hint2)."'";
if (!empty($desc_parts)) {
$desc_parts[] = "last_modified = NOW()";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so description gets last modified explicitely

Comment thread okapi/services/caches/update/docs.xml Outdated
<p>New cache status. Must be one of: Available, Temporarily unavailable, Archived.</p>
</opt>
<opt name='alt_wpts'>
<p>JSON array of waypoints. Replaces all existing waypoints with the provided list.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please emphasize the "replace"

{
if (Settings::get('OC_BRANCH') == 'oc.de') {
Db::query("DELETE FROM coordinates WHERE cache_id = '".Db::escape_string($cache_id)."' AND type = 1");
} else {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this already supports other branches?

{
# This is a simplified implementation; actual deletion logic depends on how waypoints are named
# For now, we'll just log/acknowledge this
# Full implementation would require matching waypoint names to database records

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so ... not yet implemented

Comment thread okapi/services/caches/update/docs.xml Outdated
<p>JSON array of waypoints to add without removing existing waypoints.</p>
</opt>
<opt name='remove_alt_wpts'>
<p>Pipe-separated list of waypoint names to remove (e.g., "P1|S1").</p>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apparently not yet implemented in the code?


if (Settings::get('OC_BRANCH') == 'oc.de') {
$type_map = [
'parking' => 1,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will refactor this too, in the future :-)

)
");
} else {
$type_map = [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will have to compare OCPL code in detail.
I would leave these else {} branches commented out for now.

Comment thread okapi/services/caches/delete/docs.xml Outdated
<li>All existing log entries remain in the system as historical records (they are NOT deleted)</li>
<li>Users can still view the listing and its history (read-only access)</li>
</ul>
<p>The term "Delete" here means: retire the cache from active play. The listing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend explicitely renaming this method and everything connected to it to "archive".
"Delete" as terminology means something completely different.

# Set status to Archived. The DB triggers (cachesBeforeUpdate/cachesAfterUpdate)
# handle last_modified, listing_last_modified, search index updates,
# and notifications automatically.
Db::execute("

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume last modified is handled by a trigger?

UPDATE caches
SET status = '".Db::escape_string($archived_id)."'
WHERE cache_id = '".Db::escape_string($cache['cache_id'])."'
");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OCDE shows nothing in the logs when archiving? (website logic?)

@andrixnet

Copy link
Copy Markdown
Contributor

As I commented in the code, please rename "caches/delete" -> "caches/archive".

Also, in the spirit of the "archive" method, "caches/enable" and "caches/disable" should be rather easy to implement.
In reference to #637

Also, should we exclude create and edit, the arhive/enable/disable should be easy to deploy across branches.
I'll oook in the OCPL code for more references and study the triggers again.

@andrixnet

Copy link
Copy Markdown
Contributor

Question 1: assuming more is done by OKAPI, some options present themselves.
a) do not implement extensive cache data modifications
b) implement cache data modifications into OKAPI, websites retain their implementation, which can be more extensive / refined; with the caveat that both sets of code must be worked upon should anything change (though very small chance of that)
c) implement cache data modifications into OKAPI, website move their core editing via OKAPI

At this time I would opt for b) . There would be more code maintenance overhead, but website has primary control over it's data.
Furthermore I would implement controls for the website to tell OKAPI via some configuration directive that it allows such modifications (create/edit) or not.
Website retains full control, including like OC TEAM powers which I would not delegate in the field as such.
The aim I presume is to allow user in the field to perform the minimum set of operations on his/her own caches.

Question 2: I would fold into a single method. Existing apps continue to use existing support, they can extend functionality according to available support advertized by the API.
otherwise what? make the app use 2 methods to update a cache description and a cache password? not nice.

Please comment the else part of "if branch = ocde {} else { }" for now, I mean for the initial deployment.

@hxdimpf

hxdimpf commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Thanks a lot, @andrixnet, for the careful review. Your questions led to real fixes. They are all in one new commit, e2c6fd7. The PR stays a draft until #654 to #656 are merged.

"Three questions": my mistake, sorry. There are only two. The third was about the has_cache_editing flag, which is now in the PR. I'll correct the description.

Question 1: for now, option b: the website keeps its own code and full control, and OC team actions stay out of OKAPI. The PR now follows the website's rules more closely (see below). Longer term I'd like to get to your option c: one place that holds the rules and writes the data, with the websites using it too. Then rules like "a status change writes a log" exist once instead of in the website, in OKAPI and in triggers. That's a bigger discussion than this PR. I've tried to write these methods so they could be that one place later; for example, archive/enable/disable already reuse logs/submit instead of repeating its rules. About a setting that lets a site turn create/edit off: today only the OCDE code exists, so the flag is tied to the OCDE branch. Once a second installation supports it, I'd make the flag a setting. Or would you like the setting right away?

Question 2: I'd rather not fold caches/update into caches/edit. The behaviour of an existing method shouldn't change, because existing clients rely on what caches/edit accepts and returns. So caches/edit stays exactly as it is and remains the method for the log password. caches/update doesn't touch the password. If you'd prefer that one call can do everything, I can add an optional password parameter to caches/update as well. caches/edit would still be left unchanged.

delete → archive, plus enable/disable (#637): done. There are now services/caches/archive, services/caches/disable and services/caches/enable. They are shortcuts for services/logs/submit with the log types "Archived", "Temporarily unavailable" and "Ready to search". That means:

  • they write a status log entry, as the website does when an owner changes the status (that answers your question on delete: before, it changed the status without a log);
  • they follow the same rules as logs/submit on every installation, OCPL included (owner only, OCPL refuses to unarchive, and so on), with the same response;
  • they are not OCDE-specific and don't depend on has_cache_editing;
  • caches/update no longer changes the status. Before, it could also have changed the status of a cache the OC team had locked; that is gone with it.

I tested them on OCPL. With the test user as the owner of a test cache:

  • disable wrote log type 11, and enable wrote type 10;
  • enable on an available cache became a Comment, as OCPL does;
  • archive wrote type 9;
  • enable on the archived cache was refused.

Sending back unchanged fields: caches/update now compares every value with the stored one and writes only what differs. It returns the list of parameters it changed. A client that sends back everything it read changes nothing, so there's no new last_modified and no rewritten description.

Your inline points:

  • !== '' on latitude/longitude: you were right, an empty value became 0. Now every value is checked:
    • latitude and longitude must be given together, and 0|0 is refused;
    • difficulty and terrain must be 1 to 5 in steps of 0.5 (they were silently clamped before);
    • name and size can't be emptied;
    • an empty value only removes the short description or the hint.
  • Size and waypoint type maps: the size now uses Okapi::cache_size2_to_sizeid (the local map had other wrong). Waypoint types are checked against the OCDE types; unknown types are refused instead of being stored as an invalid type.
  • last_modified: on OCDE the triggers on caches and cache_desc maintain it. The explicit last_modified = NOW() on the description is gone.
  • alt_wpts: the docs now say in bold that it replaces all waypoints. It can't be combined with add_alt_wpts or remove_alt_wpts.
  • "Not yet implemented": remove_alt_wpts now works. It takes the waypoint names that services/caches/geocache returns in alt_wpts. An unknown name is refused, and nothing is changed.
  • The else branches for other installations: these could never run, because the methods refuse non-OCDE installations first. I removed them instead of commenting them out. When OCPL support comes, it should be written against the OCPL code anyway.

Also fixed:

  • Descriptions and hints: create and update stored them unprocessed. New description_format parameter (plaintext, the default, or html):
    • plain text is escaped;
    • HTML goes through the website's purifier, exactly as log comments in logs/submit do;
    • the hint is escaped the way the website escapes it.
  • update changes one description language, not all of them at once.
  • create:
    • difficulty, terrain and size are now required, as on the website;
    • the log password is stored (it was documented but ignored);
    • a future hidden date gives "Temporarily unavailable", as on the website;
    • the URL in the response was wrong;
    • the internal cache_id is no longer returned.
  • success/message are gone from the create and update responses, as jrmajor suggested for User coordinates: save_user_coords and the user_coords field #654.

Tested with the suite on OCDE and both OCPL installations (MariaDB 10.11 and 10.0), read-only and --write, with 0 failures. The new checks:

  • an invalid difficulty is refused;
  • unchanged values change nothing;
  • removing a waypoint by name works;
  • the plain-text description is escaped;
  • a status change on someone else's cache is refused.

My OCDE test setup runs OKAPI without the website, so logs/submit, and with it archive/disable/enable, can't write logs there. Those were tested on OCPL, as described above. All changed PHP files pass php -l on PHP 7.4.

@hxdimpf hxdimpf changed the title Owners create, update and archive their caches: services/caches/create|update|delete (OCDE) Owners manage their caches: services/caches/create|update (OCDE), archive|disable|enable Oct 20, 2026
@andrixnet

Copy link
Copy Markdown
Contributor

I've seen the code being folded into sevearl common functions. Well done.

Question 1:

  • for now let it remain at variant b), further discussions on the matter are needed and they far exceed the scope here.
  • right now it is OCDE specific. Let it remain as such and after deployment to production and implementation in c:geo and feedback from users let us meet again and think about expanding.
  • if and when we should extend to OCPL, that is when I shall ask for the config flag for per site enabled or not.

Question 2:
indeed, caches/edit remains as is, unmodified. I understand.
However IMO this should not be a reason not to implement updating log_passwd in caches/update. not doing so only complicates future app development. New apps would still have to implement 2 different methods to handle cache editing & log_passwd. If caches/update supports the entire gamut it is much easier. And gives the option for existing apps to feature improve in a simpler way.

On changing status I presume the log is generated using the website default language, - this would be desireable on OCPL, but in OCDE case it should follow the website view the users prefers. (ie generate a log in french, not german, for someone who is using the french view and is a french player).

Actually IMO "status=enable" on an available cache should fail verification and return error. Else it really is a spurious log. Example: how many times do I enable an available cache and get log spammed?
Same, "status=disable" on a disabled cache should fail verification and return error.
"status=enable" on status=blocked or status=archived should fail verification.

I haven't seen yet the logs/submit HTML purifier, I am just wandering if it is the same as applied to descriptions by the website or with more or less strict purification. Asking because I had problems with OCPL description HTML purifier (website) killing some actually useful features.

How are smileys like : - ) (remove spaces) handled?

Don't know the OCDE publishing rules, but OCPL has several configurable rules on how to allow publishing.
(ie always, after X finds, always reviewd and such). If OCDE has similar rules, this OKAPI code should implement it.

@hxdimpf

hxdimpf commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, @andrixnet. Answers in order; the code changes are in 4eb4048, 7ad336a, 142e260 and 487a661.

Question 1: agreed: option b, OCDE only for now, and a per-site setting when OCPL wants it.

Question 2: agreed. caches/update now takes passwd as well, with the same rule as caches/edit (maximum length; an empty value removes the password). caches/edit stays as it is for the clients that use it.

Language of status logs: OKAPI writes no text of its own into a status log. The text is exactly what the owner sends as comment, so it's in the owner's language. Without a comment, the log entry has no text, which is also what the OCDE website writes on a status change (editcache.php inserts it with an empty text). The comment is now always plain text: archive, disable and enable no longer take comment_format, and HTML in the comment is stored escaped.

Spurious status logs: agreed. The three methods now refuse a change to the status the cache already has (success false, "The geocache already has this status."), so no log entry is written for nothing:

  • enable works only on a temporarily unavailable cache;
  • disable only on an available one;
  • archive only on an available or temporarily unavailable one.

Archived caches: OCDE's website lets owners unarchive (its status menu only hides statuses 4, 5 and 7), OCPL doesn't. So enable follows each site: allowed on OCDE, refused on OCPL. Blocked or unpublished caches can't be changed through these methods at all; they answer "The status of this geocache can only be changed on the website." Tested on the OCPL installation with MariaDB 10.0:

  • enable on an available cache was refused;
  • disable, enable and archive wrote log types 11, 10 and 9;
  • a second disable was refused;
  • enable on the archived cache was refused.

HTML purifier: cache descriptions in create and update are now plain text only, so these methods don't use a purifier at all. A description with formatting is written on the website, with the website's own editor and purifier. (For log comments with comment_format=html, logs/submit calls the website's purifier: OcHTMLPurifier with the website's settings on OCDE, UserInputFilter::purifyHtmlString on OCPL. So the rules there are exactly the website's.)

Smileys: both are stored as typed. OCDE turns :-) into an image when it shows a description or a log (a Smarty modifier), so nothing changes there.

Publishing rules: OCDE has no rules based on find counts or reviews. Its "hide a cache" form does have rules, and caches/create now follows them:

  • publish: now (the default), later (with activate_on and activate_hour; the website publishes it then) or notnow, as in the form;
  • the form's date rules: when published now or later, a cache must be hidden by then, and an event must take place after it;
  • the form's refusal of a second active cache at the same coordinates.

Descriptions in create/update: plain text only; description_format is gone.

  • The text is escaped with its line breaks kept and stored as HTML, like every description the website writes today.
  • update replaces a description only if the stored one is plain (as OKAPI writes it). A description with formatting, written on the website, is refused with HTTP 400 and stays as it is, so that no app removes the formatting by accident; the owner changes it on the website. All other fields can still be updated.
  • Short description and hint are plain text, as before.
  • One description language per create; further languages are added on the website.

Cache types and waypoints: create takes every cache type and every waypoint type, and every type can be sent unpublished (publish=notnow), to be finished and published on the website. One warning in the docs: on OCDE every additional waypoint is public with its coordinates, as on the website, which has no way to hide one (OCPL can). So a final waypoint gives the final away; the methods can do it, but owners shouldn't.

Tested: suite on OCDE 23 passed, 0 failed, 4 skipped. The status logs can't be written on my OCDE setup (logs/submit and cache_logs.picture under strict SQL, a separate issue). OCPL: 16 passed, 0 failed on MariaDB 10.11 and on 10.0, plus the status sequence above. All changed files pass php -l on PHP 7.4.

hxdimpf added a commit to OpencachingDeutschland/okapi that referenced this pull request Oct 20, 2026
@andrixnet

Copy link
Copy Markdown
Contributor

caches/update potential problem regarding descriptions in practical terms.
I presume most descriptions already are in HTML, even if apparently there is only a few paragraphs of text. Like
and/or

tags already ? If so, doesn't this make most descriptions non-editable?

@hxdimpf

hxdimpf commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

@andrixnet: good point, and you're right. I measured it on 526 descriptions of active caches around Stuttgart on opencaching.de:

Markup Share
real formatting (links, images, bold, lists, …) 79.5%
only <p> and <br>, without attributes 19.2%
no tags at all 1.1%

So with the rule as it was, only about 1% of the existing descriptions could have been changed.

Changed in 159ab71: caches/update now also replaces a description whose only tags are <p>, </p> and <br>, without attributes and with any character references. That's what the website's editor writes for a few paragraphs of text. A client shows such a description as plain text, with paragraphs as blank lines, and the new text is stored in OKAPI's plain form. Nothing visible is lost. This makes about 20% of today's descriptions editable.

Tested on the OCDE test installation with a cache whose description I set to website-style HTML:

  • paragraphs only: replaced;
  • bold text and a link: refused with HTTP 400 and left unchanged.

The other 80% stay website-only, by design. The main goal is to edit caches that were created through OKAPI. Whatever an app created, it can correct later, and all of those descriptions are plain.

These methods are meant for the simplest possible onboarding app on a phone: hide your first cache in the field, fix a typo afterwards, disable it while the container is missing. They are not a listing editor and won't become one. Formatting, images, attributes and further languages are what the website is for.

- services/draftlogs/upload_fieldnotes takes a field notes file as written by
  Garmin devices and apps (GC log type names, records of other platforms
  included), maps the log types to the website's draft types and stores the
  OC records as drafts; it returns their draft_ids.
- services/draftlogs/query and services/draftlogs/delete list and remove the
  user's drafts.
- services/apisrv/installation: has_draft_logs, and on OCDE cache_types and
  log_types.

OCDE only: OCPL has no draft logs table, so the services refuse to run there
and has_draft_logs is false. Tests: query on the read-only level; upload,
query and delete on --write.
log_types is the list services/logs/submit accepts, the same on every
installation, not what can be submitted there; field notes use their
own log type names. The cache type example is Multi, not Multi-cache.
services/lists/create, update, delete, query, add_caches, remove_caches and
get_caches manage the user's cache lists, as on the OCDE website.
services/apisrv/installation reports has_lists.

OCDE only (the services refuse to run elsewhere; has_lists is false there).
Tests: --write creates a list, adds and removes the cache, deletes the list.
…without success flags

- ListsCommon: ownership check, the website's name rules (unique per user, 80 characters,
  10 for public lists), plain-text description stored escaped (the website keeps HTML),
  password up to 80 characters for private lists, pipe-separated codes
- query: an array as documented; status (0/2/3) instead of is_public, the user's own
  is_watched, the password of a private list, optional include_caches with cache_codes
- create: list_watch (the code read is_watched), list_id only
- update: parameters left out keep their value, an empty description removes it, the name
  rules also on a status change; returns the updated list
- add_caches: only caches OKAPI shows, as the website allows only visible caches
- get_caches: cache_codes as an array
- docs rewritten (links via methodref), suite check extended
services/caches/create, update and delete let an owner manage their own
caches through the API: create a cache, change its listing, archive it
(delete never removes a cache, it sets the status to Archived).

OCDE only; the services refuse to run elsewhere. Tests: --write creates its
own test cache, updates it and archives it at the end; on other installations
it falls back to writeCache for the per-user checks.
services/apisrv/installation reports has_cache_editing (true on OCDE), as
has_draft_logs and has_lists do for their services, so clients can check
before offering cache editing. The three methods' docs and their error on
other installations point to the flag. Tests: the cache steps of --write
run only where the flag is set.
…site's rules

- caches/delete is now caches/archive. caches/disable and caches/enable
  join it. All three publish the owner's status log through logs/submit,
  so they behave like the existing status logs on every installation.
- caches/update no longer changes the status.
- Descriptions: plain text is escaped and HTML is purified, as for log
  comments. The hint is stored HTML-escaped, as on the website.
- Values are validated instead of defaulted or clamped. An empty value
  clears only the short description and the hint. latitude and longitude
  must be given together.
- update writes only values that differ from the stored ones and returns
  the list of changed parameters. It changes one description language.
- remove_alt_wpts removes waypoints by their alt_wpts names. Waypoint
  types are checked.
- create: difficulty, terrain and size are mandatory. It also stores the
  log password, sets the status from the hidden date, and returns the
  cache page URL.
- Removed: the unreachable non-OCDE code, the success/message fields,
  and cache_id.
…s comments; create publishes like the website; passwd in update

archive, disable and enable refuse a change to the status the geocache already
has and send the comment as plain text. create: publish now, later or not now,
the website's date rules and its duplicate-coordinates check; plain-text
descriptions are stored as HTML like the website's. update: passwd.
… formatting alone

create and update take the description as plain text; description_format is
gone. update replaces a description only if the stored one is plain, so no
client removes formatting written on the website.
Other types are created on the website: on OCDE additional waypoints are always
public, so the stages and the final of a multi can't be kept secret here.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants